enum RuleAction {
    Allow,
    Disallow
}

type RuleContent = {
    [scope:string] : {
        [state:string]: string
    }
};

class Rule {
    ruleAction:RuleAction;
    content: RuleContent = {};

    constructor(rawRule:any) {
        if (!rawRule.action) throw Error("no action in rule");
        this.ruleAction = rawRule.action == "allow" ? RuleAction.Allow : RuleAction.Disallow;
    
        for (const scope in rawRule) {
            if (scope=="action") continue;
            if (!this.content[scope]) this.content[scope] = {};

            for (let state in rawRule[scope]) {
                this.content[scope][state] = rawRule[scope][state];
            }
        }        
    }

    check(data:RuleContent):boolean {
        const results:boolean[] = [];

        for (let scope in this.content) {
            if (!data[scope]) {
                results.push(false);
                continue;
            }

            for (let state in this.content[scope]) {
                if (!data[scope][state]) {
                    results.push(false);
                    continue;
                }

                if (!(data[scope][state] == this.content[scope][state])) {
                    results.push(false);
                    continue;
                }
                results.push(true);
            }
        }

        return results.indexOf(this.ruleAction == RuleAction.Allow ? false : true) == -1;

    }
}

class Rules {
    static parseRules(rawRules:any):Rule[] {
        if (!Array.isArray(rawRules)) return [];
        return rawRules.map(rawRule=>new Rule(rawRule));
    }   

    static check(rules:Rule[], data:RuleContent):boolean {
        for (let index = 0; index < rules.length; index++) {
            if (!rules[index].check(data)) return false;
        }
        return true;
    }
}

export {
    Rule,
    Rules,
    RuleContent
}